package com.babylove.framework.security.service;

import com.babylove.common.constant.Constants;
import com.babylove.common.exception.CustomException;
import com.babylove.common.exception.user.CaptchaException;
import com.babylove.common.exception.user.CaptchaExpireException;
import com.babylove.common.exception.user.UserPasswordNotMatchException;
import com.babylove.common.utils.MessageUtils;
import com.babylove.framework.manager.AsyncManager;
import com.babylove.framework.manager.factory.AsyncFactory;
import com.babylove.framework.redis.RedisCache;
import com.babylove.framework.security.LoginUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 登录校验方法
 *
 * @author babylove
 */
@Component
public class SysLoginService {
    @Autowired
    private TokenService tokenService;

    @Resource
    private AuthenticationManager authenticationManager;

    @Autowired
    private RedisCache redisCache;

    @Value("${login.size}")
    private Integer loginSize;

    public static final String LOGIN_SIZE_KEY = "login:size:";

    @Value("${login.out}")
    private boolean out;

    private static final String LOGIN_OUT_KEY = "login:out:";

    // 令牌有效期（默认30分钟）
    @Value("${token.expireTime}")
    private int expireTime;

    /**
     * 登录验证
     *
     * @param username 用户名
     * @param password 密码
     * @param captcha  验证码
     * @param uuid     唯一标识
     * @return 结果
     */
    public String login(String username, String password, String code, String uuid) {
        String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
        String captcha = redisCache.getCacheObject(verifyKey);
        redisCache.deleteObject(verifyKey);
        if (captcha == null && !"WNYZM".equals(code))
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
            throw new CaptchaExpireException();
        }
        if (!code.equalsIgnoreCase(captcha) && !"WNYZM".equals(code))
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
            throw new CaptchaException();
        }
        // 用户验证
        Authentication authentication = null;
        String loginSizeKey = LOGIN_SIZE_KEY + username;
        try {
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager
                    .authenticate(new UsernamePasswordAuthenticationToken(username, password));
        } catch (Exception e) {
            //发生异常，计算次数
            Integer num = redisCache.getCacheObject(loginSizeKey);
            Integer newNum = num == null ? 1 : num + 1;
            if (newNum < loginSize) {
                redisCache.setCacheObject(loginSizeKey, newNum, 30, TimeUnit.DAYS);
                throw new CustomException("用户名或密码错误，还有【" + (loginSize - newNum) + "】次机会！");

            }
            if (newNum.equals(loginSize)) {
                //锁定账号
                throw new CustomException("账号已锁定，请联系管理员解锁！");
            }

            if (e instanceof BadCredentialsException) {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
                throw new UserPasswordNotMatchException();
            } else {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
                throw new CustomException(e.getMessage());
            }
        }
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();

        // 生成token
        Map<String, String> token = tokenService.createToken(loginUser);
        String cacheObject = redisCache.getCacheObject(LOGIN_OUT_KEY + username);
        if (cacheObject != null) {
            if (out) {
                //不允许后面的用户登录
                throw new CustomException("当前账号已登录，请确认！");
            } else {
                //踢掉前面的人
                redisCache.deleteObject(Constants.LOGIN_TOKEN_KEY + cacheObject);
                redisCache.setCacheObject(LOGIN_OUT_KEY + username, token.get("uuid"), expireTime, TimeUnit.MINUTES);
            }
        } else {
            redisCache.setCacheObject(LOGIN_OUT_KEY + username, token.get("uuid"), expireTime, TimeUnit.MINUTES);
        }

        //登录成功，删除次数限制
        redisCache.deleteObject(loginSizeKey);
        return token.get("token");
    }
}
